﻿using System;

namespace MathService.DSP.Signals
{
	public partial class DigitalSignal
	{
		#region Математика

		/// <summary>Оператор суммы цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Первое слогаемое - цифровой сигнал</param>
		/// <param name="Value">Второе слогаемое - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый сумме исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator +(DigitalSignal s, double Value)
		{
			var lv_Result = new DigitalSignal(s.dx, s.x0, s.SamplesCount);
			var lv_NewSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = lv_Samples[i] + Value;

			return lv_Result;
		}

		/// <summary>Оператор суммы цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Первое слогаемое - цифровой сигнал</param>
		/// <param name="Value">Второе слогаемое - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый сумме исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator +(double Value, DigitalSignal s) { return s + Value; }

		/// <summary>Оператор разности цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Уменьшаемое - цифровой сигнал</param>
		/// <param name="Value">Вычитаемое - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый разности исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator -(DigitalSignal s, double Value)
		{
			var lv_Result = new DigitalSignal(s.dx, s.x0, s.SamplesCount);
			var lv_NewSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = lv_Samples[i] - Value;

			return lv_Result;
		}

		/// <summary>Оператор разности цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Уменьшаемое - цифровой сигнал</param>
		/// <param name="Value">Вычитаемое - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый разности исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator -(double Value, DigitalSignal s)
		{
			var lv_Result = new DigitalSignal(s.dx, s.x0, s.SamplesCount);
			var lv_NewSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = Value - lv_Samples[i];

			return lv_Result;
		}


		/// <summary>Оператор произведения цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Первый сомножитель - цифровой сигнал</param>
		/// <param name="Value">Вторй сомножитель - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый произведению исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator *(DigitalSignal s, double Value)
		{
			var lv_Result = new DigitalSignal(s.dx, s.x0, s.SamplesCount);
			var lv_NewSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = lv_Samples[i] * Value;

			return lv_Result;
		}

		/// <summary>Оператор произведения цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Первый сомножитель - цифровой сигнал</param>
		/// <param name="Value">Вторй сомножитель - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый произведению исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator *(double Value, DigitalSignal s)
		{
			return s * Value;
		}

		/// <summary>Оператор отношения цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Делимое - цифровой сигнал</param>
		/// <param name="Value">Делитель - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый отношению исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator /(DigitalSignal s, double Value)
		{
			var lv_Result = new DigitalSignal(s);
			var lv_NewSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = lv_Samples[i] / Value;

			return lv_Result;
		}

		/// <summary>Оператор отношения цифрового сигнала и числа двойной точности</summary>
		/// <param name="s">Делимое - цифровой сигнал</param>
		/// <param name="Value">Делитель - число двойной точности</param>
		/// <returns>Цифровой сигнал, равноый отношению исходного цифрового сигнала и числа</returns>
		public static DigitalSignal operator /(double Value, DigitalSignal s)
		{
			var Result = new DigitalSignal(s);
			var lv_NewSamples = Result.Samples;
			var lv_Samples = s.Samples;

			var i_Length = lv_NewSamples.Length;
			for(var i = 0; i < i_Length; i++)
				lv_NewSamples[i] = Value / lv_Samples[i];

			return Result;
		}

		/// <summary>Оператор суммы двух сигналов</summary>
		/// <param name="s1">Первое слогаемое - цифровой сигнал</param>
		/// <param name="s2">Второе слогаемое - цифровой сигнал</param>
		/// <returns>Цифровой сигнал - сумма двух сигналов</returns>
		public static DigitalSignal operator +(DigitalSignal s1, DigitalSignal s2)
		{
			if(s1 == null)
				if(s2 == null)
					throw new ArgumentNullException("Незаданы оба сигнала");
				else
					return new DigitalSignal(s2);
			if(s2 == null)
				return new DigitalSignal(s1);


			DigitalSignal lv_Result;

			if(s1.dx != s2.dx ||
				s1.f_Samples.Length != s2.f_Samples.Length ||
				s1.x0 != s2.x0)
			{
				lv_Result = new DigitalSignal(s1, s2);
				var x0 = lv_Result.x0;
				var x = x0;
				var dx = lv_Result.dx;
				var i_Length = lv_Result.SamplesCount;
				for(var i = 0; i < i_Length; i++)
				{
					x = x0 + i * dx;
					lv_Result[i] = s1[x] + s2[x];
				}
				return lv_Result;
			}

			lv_Result = new DigitalSignal(s1.dx, s1.x0, s1.SamplesCount);
			var lv_ResultSamples = lv_Result.Samples;
			var lv_S1Samples = s1.Samples;
			var lv_S2Samples = s2.Samples;

			var i_S1Length = lv_S1Samples.Length;
			for(var i = 0; i < i_S1Length; i++)
				lv_ResultSamples[i] = lv_S1Samples[i] + lv_S2Samples[i];

			return lv_Result;
		}

		/// <summary>Оператор разности двух сигналов</summary>
		/// <param name="s1">Уменьшаемое - цифровой сигнал</param>
		/// <param name="s2">Вычитаемое - цифровой сигнал</param>
		/// <returns>Цифровой сигнал - сумма двух сигналов</returns>
		public static DigitalSignal operator -(DigitalSignal s1, DigitalSignal s2)
		{
			if(s1 == null)
				if(s2 == null)
					throw new ArgumentNullException("Незаданы оба сигнала");
				else
					return 0 - s2;
			if(s2 == null)
				return new DigitalSignal(s1);

			DigitalSignal lv_Result;

			if(s1.f_dx != s2.f_dx ||
				s1.f_Samples.Length != s2.f_Samples.Length ||
				s1.f_x0 != s2.f_x0)
			{
				lv_Result = new DigitalSignal(s1, s2);
				var x0 = lv_Result.x0;
				var dx = lv_Result.dx;
				var i_Length = lv_Result.SamplesCount;
				for(var i = 0; i < i_Length; i++)
				{
					var x = x0 + i * dx;
					lv_Result[i] = s1[x] - s2[x];
				}
				return lv_Result;
			}

			lv_Result = new DigitalSignal(s1.dx, s1.x0, s1.SamplesCount);
			var lv_ResultSamples = lv_Result.Samples;
			var lv_S1Samples = s1.Samples;
			var lv_S2Samples = s2.Samples;

			var i_S1Length = lv_S1Samples.Length;
			for(var i = 0; i < i_S1Length; i++)
				lv_ResultSamples[i] = lv_S1Samples[i] - lv_S2Samples[i];

			return lv_Result;
		}

		public static DigitalSignal operator *(DigitalSignal s1, DigitalSignal s2)
		{
			DigitalSignal lv_Result;

			if(s1.f_dx != s2.f_dx ||
				s1.f_Samples.Length != s2.f_Samples.Length ||
				s1.f_x0 != s2.f_x0)
			{
				lv_Result = new DigitalSignal(s1, s2);
				var x0 = lv_Result.x0;
				var dx = lv_Result.dx;
				for(int i = 0, i_Length = lv_Result.SamplesCount; i < i_Length; i++)
				{
					var x = x0 + i * dx;
					lv_Result[i] = s1[x] * s2[x];
				}
				return lv_Result;
			}

			lv_Result = new DigitalSignal(s1.dx, s1.x0, s1.SamplesCount);
			var lv_ResultSamples = lv_Result.Samples;
			var lv_S1Samples = s1.Samples;
			var lv_S2Samples = s2.Samples;

			var i_S1Length = lv_S1Samples.Length;
			for(var i = 0; i < i_S1Length; i++)
				lv_ResultSamples[i] = lv_S1Samples[i] * lv_S2Samples[i];

			return lv_Result;
		}

		public static DigitalSignal operator /(DigitalSignal s1, DigitalSignal s2)
		{
			DigitalSignal lv_Result;

			if(s1.f_dx != s2.f_dx ||
				s1.f_Samples.Length != s2.f_Samples.Length ||
				s1.f_x0 != s2.f_x0)
			{
				lv_Result = new DigitalSignal(s1, s2);
				var x0 = lv_Result.x0;
				var dx = lv_Result.dx;
				var i_Length = lv_Result.SamplesCount;
				for(var i = 0; i < i_Length; i++)
				{
					var x = x0 + i * dx;
					lv_Result[i] = s1[x] / s2[x];
				}
				return lv_Result;
			}

			lv_Result = new DigitalSignal(s1.dx, s1.x0, s1.SamplesCount);
			var lv_ResultSamples = lv_Result.Samples;
			var lv_S1Samples = s1.Samples;
			var lv_S2Samples = s2.Samples;

			var i_S1Length = lv_S1Samples.Length;
			for(var i = 0; i < i_S1Length; i++)
				lv_ResultSamples[i] = lv_S1Samples[i] / lv_S2Samples[i];

			return lv_Result;
		}

		#endregion

		public static DigitalSignal operator |(DigitalSignal s1, DigitalSignal s2)
		{
			return new DigitalSignal(s1.dx, s1.x0, s1.Samples.Concatinate(s2.Samples));
		}

		public static DigitalSignal operator |(DigitalSignal s, double Value)
		{
			s.AddValue(Value);
			return s;
		}

		#region Операторы преобразования

		public static implicit operator double[](DigitalSignal s)
		{
			var lv_Values = new double[s.f_Samples.Length];
			s.f_Samples.CopyTo(lv_Values, 0);
			return lv_Values;
		}

		public static implicit operator DigitalSignal(ArraySegment<double> Values)
		{
			return new DigitalSignal(1, (double[])Values.Array.Clone());
		}

		#endregion

		#region Операторы сравнения

		public static bool operator ==(DigitalSignal s1, DigitalSignal s2)
		{
			return ((object)s1) == null ? ((object)s2) == null : ((object)s2) != null && s1.Equals(s2);
		}

		public static bool operator !=(DigitalSignal s1, DigitalSignal s2)
		{
			return !(s1 == s2);
		}

		#endregion

		public static DigitalSignal operator *(DigitalSignal s, Func<double, double> f)
		{
			var lv_Result = new DigitalSignal(s);
			var lv_ResultSamples = lv_Result.Samples;
			var lv_Samples = s.Samples;
			for(var i = 0; i < lv_ResultSamples.Length; i++)
				lv_ResultSamples[i] = f(lv_Samples[i]);
			return lv_Result;
		}
	}
}
